;*****************************************************************************************************
;+
; NAME:
;       IGL_RANGETOOL
;
; PURPOSE:
;
;       This is a CATTOOL object representing a data range
;       modification operation (pan and zoom) in an IGL_Window.
;       It is written to be able to undo/redo the change of data
;       range. Only IGL_Graphic objects can be the target of this tool.
;
;
; AUTHORS:
;
;        RAPHAEL SADOUN
;        Graduate Student
;        Institut d'Astrophysique de Paris
;        98 bis boulevard Arago
;        75014 PARIS FRANCE
;        E-mail: raphael.sadoun@gmail.com
;
; SUPERCLASSES:
;
;       CATTOOL (Catalyst Library)
;
; SYNTAX:
;
;       tool = Obj_New('IGL_RangeTool')
;
;
; CLASS_STRUCTURE:
;
;       class = {IGL_RangeTool, $      
;                INHERITS CATTOOL $   ; Inherits the CATTOOL object class.
;               }
;
;
; MODIFICATION_HISTORY:
;
;       Written by: Raphael Sadoun, 24 April 2012
;-
;*****************************************************************************************************
;+
; NAME:
;       IGL_RangeTool::APPLY
;
; PURPOSE:
;
;       This method apply the tool. It saves the graphic current data
;       ranges of all the different coordinate systems contained in it
;       and reassign the previously stored one.
;
; SYNTAX:
;
;       self -> Apply
;
; ARGUMENTS:
;
;       None.
;
; KEYWORDS:
;
;       DRAW:   Set this keyword to call the draw method of the target
;               object after the tool is applied.
;
;-
;*****************************************************************************************************
PRO IGL_RangeTool::Apply, Draw=draw

   @cat_pro_error_handler

   ;; Make sure there is a valid target object.
   IF Obj_ISA_Valid(self._theTarget,'IGL_Graphic') EQ 0 THEN RETURN

   ;; Make sure there is data in the cache
   IF Ptr_Valid(self._theCache) EQ 0 THEN RETURN
   
   ;; Get the data in the cache
   data = *self._theCache
   objects = data.refs
   ranges = data.ranges

   ;; Save the current coordinate systems and their data range
   self -> CacheRanges

   ;; Reassign the previously stored data ranges to the coordinate
   ;; systems in the graphic
   FOR i=0, n_elements(objects)-1 DO BEGIN
            
      IF (OBJ_ISA_VALID(objects[i],'CatCoord')) THEN BEGIN

         xrange = ranges[0:1,i]
         yrange = ranges[2:3,i]               
         objects[i] -> SetProperty, Xrange=xrange, Yrange=yrange
         
      ENDIF
            
   ENDFOR

   ;; Need a draw method?
   IF Keyword_Set(draw) THEN self._theTarget -> Draw

   ;; Report completion
   self -> Report, /Completed

END

;*****************************************************************************************************
;+
; NAME:
;       IGL_RangeTool::CACHERANGES
;
; PURPOSE:
;
;       This method stores the current data ranges of the coordinate
;       systems in the cache.
;
; SYNTAX:
;
;       self -> CacheRanges
;
; ARGUMENTS:
;
;       None.
;
; KEYWORDS:
;
;       None.
;
;-
;*****************************************************************************************************
PRO IGL_RangeTool::CacheRanges

  @cat_pro_error_handler

  ;; Make sure we have a valid target
  IF OBJ_ISA_VALID(self._theTarget,'IGL_Graphic') EQ 0 THEN RETURN
  
  ;; Get all the different coordinate systems of the graphic. Each
  ;; direct child of the graphic must be either a IGL_BaseRangeObject
  ;; or an IGL_Image and is associated with a different coordinate system.
  children = self._theTarget -> Get(/ALL,ISA=['IGL_BASERANGEOBJECT','IGL_IMAGE'],count=count)

  ;; Nothing to do if there is nothing in the graphic
  IF (count EQ 0) THEN RETURN
  
  ;; Create the structure that will be stored in the cache. We need to
  ;; keep track of the coordinate system object references and their
  ;; data ranges (x and y ranges).
  dataToCache = {refs:objarr(count),ranges:fltarr(4,count)}
        
  FOR i=0, count-1 DO BEGIN
                        
     children[i] -> GetProperty, COORD_OBJECT=coords

     IF OBJ_ISA_VALID(coords,'CatCoord') THEN BEGIN
        coords -> GetProperty, Xrange=xr, Yrange=yr
        dataToCache.refs[i] = coords
        dataToCache.ranges[*,i] = [xr,yr]
     ENDIF
           
  ENDFOR

  ;; Save the structure in the cache
  self -> Cache, dataToCache

  ;; Report completion
  self -> Report, /Completed
     
END

;*****************************************************************************************************
;+
; NAME:
;       IGL_RangeTool::COMPARECACHEDATA
;
; PURPOSE:
;
;       This method compares the current data ranges of the coodinate
;       systems in the graphic with their previously stored ones. It
;       returns 1 if the data ranges have changed and 0 if they are
;       the same.
;
; SYNTAX:
;
;       HasChanged = self -> CompareCacheData()
;
; ARGUMENTS:
;
;       None.
;
; KEYWORDS:
;
;       None.
;
;-
;*****************************************************************************************************
FUNCTION IGL_RangeTool::CompareCacheData

  @cat_func_error_handler

  ;; Make sure we have a valid target object
  IF (obj_isa_valid(self._theTarget,'IGL_Graphic') EQ 0) THEN RETURN, 0

  ;; Make sure we have data in the cache
  IF Ptr_Valid(self._theCache) EQ 0 THEN RETURN, 0

  ;; Get the data in the cache
  self -> GetProperty, cached_data=data
  objects = data.refs
  ranges = data.ranges

  ;; Compare previous and current data ranges
  FOR i=0, n_elements(objects)-1 DO BEGIN         
           
     IF OBJ_ISA_VALID(objects[i],'CatCoord') THEN BEGIN
              
        objects[i] -> GetProperty, xrange=xr, yrange=yr
        xrange = ranges[0:1,i]
        yrange = ranges[2:3,i]
        
        IF (ARRAY_EQUAL(xrange,xr) EQ 0) THEN RETURN, 1
        IF (ARRAY_EQUAL(yrange,yr) EQ 0) THEN RETURN, 1
              
     ENDIF
           
  ENDFOR

  ;; Report completion
  self -> Report, /Completed

  RETURN, 0

END

;*****************************************************************************************************
;+
; NAME:
;       IGL_RangeTool::GETPROPERTY
;
; PURPOSE:
;
;       This method is used to get the object's properties.
;
; SYNTAX:
;
;       self -> GetProperty
;
; ARGUMENTS:
;
;     None.
;
; KEYWORDS:
;
;     _REF_EXTRA:     Any keywords appropriate for the superclass GetProperty method.
;-
;*****************************************************************************************************
PRO IGL_RangeTool::GetProperty, _REF_EXTRA=extraKeywords

   @cat_pro_error_handler

   IF (N_ELEMENTS (extraKeywords) GT 0) THEN self -> CATTOOL::GetProperty, _EXTRA=extraKeywords

   self -> Report, /Completed

END

;*****************************************************************************************************
;+
; NAME:
;       IGL_RangeTool::REDO
;
; PURPOSE:
;
;       This method redo the tool on the target object.
;
; SYNTAX:
;
;       self -> Redo
;
; ARGUMENTS:
;
;       None.
;
; KEYWORDS:
;
;       DRAW:    Set this keyword to call the draw method of the
;                target object after the tool is applied.
;
;-
;*****************************************************************************************************
PRO IGL_RangeTool::Redo, Draw=draw

   @cat_pro_error_handler

   self -> Apply, Draw=draw

   self -> Report, /Completed

END

;*****************************************************************************************************
;+
; NAME:
;       IGL_RangeTool::SETPROPERTY
;
; PURPOSE:
;
;       This method is used to set the object's properties.
;
; SYNTAX:
;
;       self -> SetProperty
;
; ARGUMENTS:
;
;     None.
;
; KEYWORDS:
;
;     _EXTRA:       Any keyword accepted by the SetProperty method of
;                   the CATTOOL superclass.
;-
;*****************************************************************************************************
PRO IGL_RangeTool::SetProperty, _EXTRA=extraKeywords

   @cat_pro_error_handler

   IF (N_ELEMENTS (extraKeywords) GT 0) THEN self -> CATTOOL::SetProperty, _EXTRA=extraKeywords

   self -> Report, /Completed

END


;*****************************************************************************************************
;+
; NAME:
;       IGL_RangeTool::UNDO
;
; PURPOSE:
;
;       This method undo the tool.
;
; SYNTAX:
;
;       self -> Undo
;
; ARGUMENTS:
;
;       None.
;
; KEYWORDS:
;
;       DRAW:    Set this keyword to call the draw method of the
;                target object after the tool is applied.
;
;-
;*****************************************************************************************************
PRO IGL_RangeTool::Undo, DRAW=draw

   @cat_pro_error_handler

   self -> Apply, Draw=draw

   self -> Report, /Completed

END


;*****************************************************************************************************
;+
; NAME:
;       IGL_RangeTool::CLEANUP
;
; PURPOSE:
;
;       This is the IGL_RangeTool object class destructor method.
;
; SYNTAX:
;
;       Called automatically when the object is destroyed.
;
;
; ARGUMENTS:
;
;     None.
;
; KEYWORDS:
;
;     None.
;-
;*****************************************************************************************************
PRO IGL_RangeTool::CLEANUP

   @cat_pro_error_handler

   self -> CATTOOL::CLEANUP

   self -> Report, /Completed
   
END


;*****************************************************************************************************
;+
; NAME:
;       IGL_RangeTool::INIT
;
; PURPOSE:
;
;       This is the IGL_RangeTool object class creator method.
;
; SYNTAX:
;
;       Called automatically when the object is created.
;
; ARGUMENTS:
;
;     None.
;
; KEYWORDS:
;
;     _EXTRA:    Any keywords accepted by the CATTOOL::Init method.
;
;-
;*****************************************************************************************************
FUNCTION IGL_RangeTool::INIT, Target=target, $
                              _Extra=extraKeywords

   @cat_func_error_handler

   ;; The target must be a valid IGL_Graphic object.
   IF N_Elements(target) NE 0 THEN BEGIN
     IF (Obj_IsA_Valid(target, 'IGL_Graphic')) EQ 0 THEN message, 'The target object is not a valid IGL_Graphic object.'
   ENDIF

   ; Call superclass INIT method
   ok = self -> CATTOOL::Init(Target=target,_Extra=extraKeywords)

   IF (NOT ok) THEN RETURN, 0

   ;; Report completion
   self -> Report, /completed

   RETURN, 1

END

;*****************************************************************************************************
;
; NAME:
;       IGL_RANGETOOL CLASS DEFINITION
;
; PURPOSE:
;
;       This is the IGL_RangeTool object's structure definition code.
;
;*****************************************************************************************************
PRO IGL_RangeTool__DEFINE, class

  COMPILE_OPT idl2, HIDDEN

  class = {IGL_RangeTool, $     
           INHERITS CATTOOL $   ; Inherits the CATTOOL object class.
          }

  struct_hide, class

END
